import { describe, expect, vi, beforeEach } from "vitest";

import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { tasker } from "@calcom/features/tasker";
import { WorkflowTriggerEvents, WorkflowActions, WorkflowTemplates, TimeUnit } from "@calcom/prisma/enums";
import { test } from "@calcom/web/test/fixtures/fixtures";

import { WorkflowService } from "./WorkflowService";

vi.mock("@calcom/features/ee/workflows/lib/reminders/reminderScheduler");
vi.mock("@calcom/features/tasker");

const mockScheduleWorkflowReminders = vi.mocked(scheduleWorkflowReminders);
const mockTasker = vi.mocked(tasker);

vi.mock("@calcom/features/profile/lib/hideBranding", () => ({
  getHideBranding: vi.fn().mockResolvedValue(false),
}));

const mockWorkflowReminderCreate = vi.fn();
vi.mock("@calcom/features/ee/workflows/repositories/WorkflowReminderRepository", () => ({
  WorkflowReminderRepository: vi.fn().mockImplementation(() => ({
    create: mockWorkflowReminderCreate,
  })),
}));

vi.mock("@calcom/prisma", () => ({
  prisma: {},
  default: {},
}));

describe("WorkflowService.scheduleFormWorkflows", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  const mockForm = {
    id: "form-123",
    userId: 101,
    teamId: null,
    fields: [
      { type: "email", identifier: "email" },
      { type: "phone", identifier: "phone" },
    ],
    user: {
      email: "formowner@example.com",
      timeFormat: 12,
      locale: "en",
    },
  };

  const mockResponses = {
    email: {
      value: "submitter@example.com",
      response: "submitter@example.com",
    },
    phone: {
      value: "+1234567890",
      response: "+1234567890",
    },
  };

  test("should call scheduleWorkflowReminders for FORM_SUBMITTED triggers with correct phone number", async () => {
    const workflows = [
      {
        id: 1,
        name: "Form Submitted",
        userId: 101,
        teamId: null,
        trigger: WorkflowTriggerEvents.FORM_SUBMITTED,
        time: null,
        timeUnit: null,
        steps: [
          {
            id: 1,
            action: WorkflowActions.SMS_ATTENDEE,
            sendTo: null,
            reminderBody: "Thank you!",
            emailSubject: "Form Received",
            template: WorkflowTemplates.CUSTOM,
            verifiedAt: new Date(),
            includeCalendarEvent: false,
            numberVerificationPending: false,
            numberRequired: false,
          },
        ],
      },
    ];

    const mockCreditCheckFn = vi.fn().mockResolvedValue(true);

    await WorkflowService.scheduleFormWorkflows({
      workflows,
      responses: mockResponses,
      form: mockForm,
      responseId: 123,
      routedEventTypeId: null,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(mockScheduleWorkflowReminders).toHaveBeenCalledWith({
      smsReminderNumber: "+1234567890",
      formData: {
        responses: mockResponses,
        user: { email: "formowner@example.com", timeFormat: 12, locale: "en" },
        routedEventTypeId: null,
      },
      hideBranding: false,
      workflows: [workflows[0]],
      creditCheckFn: mockCreditCheckFn,
    });
  });

  test("should create task for FORM_SUBMITTED_NO_EVENT triggers", async () => {
    const workflows = [
      {
        id: 2,
        name: "Form Follow-up",
        userId: 101,
        teamId: null,
        trigger: WorkflowTriggerEvents.FORM_SUBMITTED_NO_EVENT,
        time: 30,
        timeUnit: TimeUnit.MINUTE,
        steps: [
          {
            id: 2,
            action: WorkflowActions.EMAIL_ATTENDEE,
            sendTo: null,
            reminderBody: "Follow up message",
            emailSubject: "Follow Up",
            template: WorkflowTemplates.CUSTOM,
            verifiedAt: new Date(),
            includeCalendarEvent: false,
            numberVerificationPending: false,
            numberRequired: false,
            sender: null,
          },
        ],
      },
    ];

    mockTasker.create.mockResolvedValue({ id: "task-123" });

    const mockCreditCheckFn = vi.fn().mockResolvedValue(true);

    await WorkflowService.scheduleFormWorkflows({
      workflows,
      responses: mockResponses,
      responseId: 123,
      form: mockForm,
      routedEventTypeId: null,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(mockTasker.create).toHaveBeenCalledWith(
      "triggerFormSubmittedNoEventWorkflow",
      {
        responseId: 123,
        responses: mockResponses,
        smsReminderNumber: "+1234567890",
        hideBranding: false,
        routedEventTypeId: null,
        submittedAt: expect.any(Date),
        form: {
          id: "form-123",
          userId: 101,
          teamId: undefined,
          user: {
            email: "formowner@example.com",
            timeFormat: 12,
            locale: "en",
          },
        },
        workflow: workflows[0],
      },
      { scheduledAt: expect.any(Date) }
    );
  });

  test("should handle forms without phone fields by passing null smsReminderNumber", async () => {
    const formWithoutPhone = {
      ...mockForm,
      fields: [
        { type: "email", identifier: "email" },
        { type: "text", identifier: "name" },
      ],
    };

    const workflows = [
      {
        id: 1,
        name: "Form Submitted",
        userId: 101,
        teamId: null,
        trigger: WorkflowTriggerEvents.FORM_SUBMITTED,
        time: null,
        timeUnit: null,
        steps: [
          {
            id: 1,
            action: WorkflowActions.EMAIL_ATTENDEE,
            sendTo: null,
            reminderBody: "Thank you!",
            emailSubject: "Form Received",
            template: WorkflowTemplates.CUSTOM,
            verifiedAt: new Date(),
            includeCalendarEvent: false,
            numberVerificationPending: false,
            numberRequired: false,
            sender: null,
          },
        ],
      },
    ];

    const mockCreditCheckFn = vi.fn().mockResolvedValue(true);

    await WorkflowService.scheduleFormWorkflows({
      workflows,
      responses: mockResponses,
      form: formWithoutPhone,
      responseId: 123,
      routedEventTypeId: null,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(mockScheduleWorkflowReminders).toHaveBeenCalledWith({
      smsReminderNumber: null,
      formData: {
        responses: mockResponses,
        user: { email: "formowner@example.com", timeFormat: 12, locale: "en" },
        routedEventTypeId: null,
      },
      hideBranding: false,
      workflows: [workflows[0]],
      creditCheckFn: mockCreditCheckFn,
    });
  });
});

describe("WorkflowService.scheduleLazyEmailWorkflow", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  test("should schedule lazy email workflow for BEFORE_EVENT trigger", async () => {
    const mockWorkflow = {
      time: 24,
      timeUnit: TimeUnit.HOUR,
    };

    const mockEvt = {
      uid: "booking-123",
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const mockWorkflowReminder = {
      id: 1,
      uuid: "reminder-uuid-123",
    };

    mockWorkflowReminderCreate.mockResolvedValue(mockWorkflowReminder);
    mockTasker.create.mockResolvedValue({ id: "task-123" });

    await WorkflowService.scheduleLazyEmailWorkflow({
      workflowTriggerEvent: "BEFORE_EVENT",
      workflowStepId: 1,
      workflow: mockWorkflow,
      evt: mockEvt,
    });

    expect(mockWorkflowReminderCreate).toHaveBeenCalledWith({
      bookingUid: "booking-123",
      workflowStepId: 1,
      method: "EMAIL",
      scheduledDate: expect.any(Date),
      scheduled: true,
    });

    expect(mockTasker.create).toHaveBeenCalledWith(
      "sendWorkflowEmails",
      {
        bookingUid: "booking-123",
        workflowReminderId: 1,
      },
      {
        scheduledAt: expect.any(Date),
        referenceUid: "reminder-uuid-123",
      }
    );
  });

  test("should schedule lazy email workflow for AFTER_EVENT trigger", async () => {
    const mockWorkflow = {
      time: 1,
      timeUnit: TimeUnit.HOUR,
    };

    const mockEvt = {
      uid: "booking-456",
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const mockWorkflowReminder = {
      id: 2,
      uuid: "reminder-uuid-456",
    };

    mockWorkflowReminderCreate.mockResolvedValue(mockWorkflowReminder);
    mockTasker.create.mockResolvedValue({ id: "task-456" });

    await WorkflowService.scheduleLazyEmailWorkflow({
      workflowTriggerEvent: "AFTER_EVENT",
      workflowStepId: 2,
      workflow: mockWorkflow,
      evt: mockEvt,
    });

    expect(mockTasker.create).toHaveBeenCalledWith(
      "sendWorkflowEmails",
      {
        bookingUid: "booking-456",
        workflowReminderId: 2,
      },
      {
        scheduledAt: expect.any(Date),
        referenceUid: "reminder-uuid-456",
      }
    );
  });

  test("should handle seated events with seatReferenceId", async () => {
    const mockWorkflow = {
      time: 24,
      timeUnit: TimeUnit.HOUR,
    };

    const mockEvt = {
      uid: "booking-789",
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const mockWorkflowReminder = {
      id: 3,
      uuid: "reminder-uuid-789",
    };

    mockWorkflowReminderCreate.mockResolvedValue(mockWorkflowReminder);
    mockTasker.create.mockResolvedValue({ id: "task-789" });

    await WorkflowService.scheduleLazyEmailWorkflow({
      workflowTriggerEvent: "BEFORE_EVENT",
      workflowStepId: 3,
      workflow: mockWorkflow,
      evt: mockEvt,
      seatReferenceId: "seat-123",
    });

    expect(mockTasker.create).toHaveBeenCalled();
  });

  test("should not schedule if bookingUid is missing", async () => {
    const mockWorkflow = {
      time: 24,
      timeUnit: TimeUnit.HOUR,
    };

    const mockEvt = {
      uid: "",
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    await WorkflowService.scheduleLazyEmailWorkflow({
      workflowTriggerEvent: "BEFORE_EVENT",
      workflowStepId: 1,
      workflow: mockWorkflow,
      evt: mockEvt,
    });

    expect(mockWorkflowReminderCreate).not.toHaveBeenCalled();
    expect(mockTasker.create).not.toHaveBeenCalled();
  });

  test("should not schedule if time is null", async () => {
    const mockWorkflow = {
      time: null,
      timeUnit: TimeUnit.HOUR,
    };

    const mockEvt = {
      uid: "booking-123",
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    await WorkflowService.scheduleLazyEmailWorkflow({
      workflowTriggerEvent: "BEFORE_EVENT",
      workflowStepId: 1,
      workflow: mockWorkflow,
      evt: mockEvt,
    });

    expect(mockWorkflowReminderCreate).not.toHaveBeenCalled();
    expect(mockTasker.create).not.toHaveBeenCalled();
  });
});

describe("WorkflowService.processWorkflowScheduledDate", () => {
  test("should calculate scheduled date for BEFORE_EVENT trigger", () => {
    const evt = {
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const result = WorkflowService.processWorkflowScheduledDate({
      workflowTriggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 24,
      timeUnit: TimeUnit.HOUR,
      evt,
    });

    expect(result).toBeDefined();
    expect(result?.toISOString()).toBe("2024-11-30T10:00:00.000Z");
  });

  test("should calculate scheduled date for AFTER_EVENT trigger", () => {
    const evt = {
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const result = WorkflowService.processWorkflowScheduledDate({
      workflowTriggerEvent: WorkflowTriggerEvents.AFTER_EVENT,
      time: 1,
      timeUnit: TimeUnit.HOUR,
      evt,
    });

    expect(result).toBeDefined();
    expect(result?.toISOString()).toBe("2024-12-01T12:00:00.000Z");
  });

  test("should return null if time is null", () => {
    const evt = {
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const result = WorkflowService.processWorkflowScheduledDate({
      workflowTriggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
      time: null,
      timeUnit: TimeUnit.HOUR,
      evt,
    });

    expect(result).toBeNull();
  });

  test("should return null if timeUnit is null", () => {
    const evt = {
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const result = WorkflowService.processWorkflowScheduledDate({
      workflowTriggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 24,
      timeUnit: null,
      evt,
    });

    expect(result).toBeNull();
  });

  test("should calculate scheduled date with MINUTE time unit", () => {
    const evt = {
      startTime: "2024-12-01T10:00:00Z",
      endTime: "2024-12-01T11:00:00Z",
    };

    const result = WorkflowService.processWorkflowScheduledDate({
      workflowTriggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 30,
      timeUnit: TimeUnit.MINUTE,
      evt,
    });

    expect(result).toBeDefined();
    expect(result?.toISOString()).toBe("2024-12-01T09:30:00.000Z");
  });
});

describe("WorkflowService.generateCommonScheduleFunctionParams", () => {
  test("should generate common parameters correctly", () => {
    const verifiedAt = new Date("2024-01-01T00:00:00Z");
    const mockWorkflow = {
      id: 1,
      name: "Test Workflow",
      trigger: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 24,
      timeUnit: TimeUnit.HOUR,
      userId: 1,
      teamId: null,
    };

    const mockWorkflowStep = {
      id: 1,
      action: WorkflowActions.EMAIL_ATTENDEE,
      sendTo: null,
      template: WorkflowTemplates.REMINDER,
      reminderBody: null,
      emailSubject: null,
      sender: null,
      includeCalendarEvent: false,
      verifiedAt,
      numberVerificationPending: false,
      numberRequired: false,
    };

    const mockCreditCheckFn = vi.fn();

    const result = WorkflowService.generateCommonScheduleFunctionParams({
      workflow: mockWorkflow,
      workflowStep: mockWorkflowStep,
      seatReferenceUid: undefined,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(result).toEqual({
      triggerEvent: WorkflowTriggerEvents.BEFORE_EVENT,
      timeSpan: {
        time: 24,
        timeUnit: TimeUnit.HOUR,
      },
      workflowStepId: 1,
      template: WorkflowTemplates.REMINDER,
      userId: 1,
      teamId: null,
      seatReferenceUid: undefined,
      verifiedAt,
      creditCheckFn: mockCreditCheckFn,
    });
  });

  test("should handle seated events with seatReferenceUid", () => {
    const verifiedAt = new Date("2024-01-01T00:00:00Z");
    const mockWorkflow = {
      id: 1,
      name: "Test Workflow",
      trigger: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 24,
      timeUnit: TimeUnit.HOUR,
      userId: 1,
      teamId: null,
    };

    const mockWorkflowStep = {
      id: 1,
      action: WorkflowActions.EMAIL_ATTENDEE,
      sendTo: null,
      template: WorkflowTemplates.REMINDER,
      reminderBody: null,
      emailSubject: null,
      sender: null,
      includeCalendarEvent: false,
      verifiedAt,
      numberVerificationPending: false,
      numberRequired: false,
    };

    const mockCreditCheckFn = vi.fn();

    const result = WorkflowService.generateCommonScheduleFunctionParams({
      workflow: mockWorkflow,
      workflowStep: mockWorkflowStep,
      seatReferenceUid: "seat-123",
      creditCheckFn: mockCreditCheckFn,
    });

    expect(result.seatReferenceUid).toBe("seat-123");
  });

  test("should handle null verifiedAt", () => {
    const mockWorkflow = {
      id: 1,
      name: "Test Workflow",
      trigger: WorkflowTriggerEvents.BEFORE_EVENT,
      time: 24,
      timeUnit: TimeUnit.HOUR,
      userId: 1,
      teamId: null,
    };

    const mockWorkflowStep = {
      id: 1,
      action: WorkflowActions.EMAIL_ATTENDEE,
      sendTo: null,
      template: WorkflowTemplates.REMINDER,
      reminderBody: null,
      emailSubject: null,
      sender: null,
      includeCalendarEvent: false,
      verifiedAt: null,
      numberVerificationPending: false,
      numberRequired: false,
    };

    const mockCreditCheckFn = vi.fn();

    const result = WorkflowService.generateCommonScheduleFunctionParams({
      workflow: mockWorkflow,
      workflowStep: mockWorkflowStep,
      seatReferenceUid: undefined,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(result.verifiedAt).toBeNull();
  });

  test("should handle team workflows", () => {
    const verifiedAt = new Date("2024-01-01T00:00:00Z");
    const mockWorkflow = {
      id: 1,
      name: "Team Workflow",
      trigger: WorkflowTriggerEvents.AFTER_EVENT,
      time: 1,
      timeUnit: TimeUnit.HOUR,
      userId: null,
      teamId: 123,
    };

    const mockWorkflowStep = {
      id: 2,
      action: WorkflowActions.EMAIL_HOST,
      sendTo: null,
      template: WorkflowTemplates.CUSTOM,
      reminderBody: "Custom body",
      emailSubject: "Custom subject",
      sender: "Team",
      includeCalendarEvent: true,
      verifiedAt,
      numberVerificationPending: false,
      numberRequired: false,
    };

    const mockCreditCheckFn = vi.fn();

    const result = WorkflowService.generateCommonScheduleFunctionParams({
      workflow: mockWorkflow,
      workflowStep: mockWorkflowStep,
      seatReferenceUid: undefined,
      creditCheckFn: mockCreditCheckFn,
    });

    expect(result).toEqual({
      triggerEvent: WorkflowTriggerEvents.AFTER_EVENT,
      timeSpan: {
        time: 1,
        timeUnit: TimeUnit.HOUR,
      },
      workflowStepId: 2,
      template: WorkflowTemplates.CUSTOM,
      userId: null,
      teamId: 123,
      seatReferenceUid: undefined,
      verifiedAt,
      creditCheckFn: mockCreditCheckFn,
    });
  });
});
